package cn.itcast.work.controller.user_manager;


import cn.itcast.work.dao.user.UserMapper;
import cn.itcast.work.pojo.*;
import cn.itcast.work.service.category.CategoryService;
import cn.itcast.work.service.commit_service.CommitService;
import cn.itcast.work.service.user_service.UserService;
import cn.itcast.work.springsecurity.CustomUserDetails;
import jakarta.annotation.Resource;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;


import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import cn.hutool.json.JSONObject;
import com.alibaba.fastjson.JSON;

import util.CustomStringJavaCompiler;
import util.Result;

@Slf4j
@Controller//添加到ioc容器
@RequestMapping("/blog")
public class UserController {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private PasswordEncoder passwordEncoder;//加密器,给新注册用户的密码加密


    @Resource(name = "userService")
    private UserService userService;

    @Resource(name = "CategoryService")
    private CategoryService categoryService;

    @Resource(name = "CommitService")
    private CommitService commitService;
    @Autowired
    private UserMapper userMapper;

    //进入首页
    @RequestMapping("/intoMain")
    public String intoMian() {
        return "main";
    }

    //进入登录接口
    @RequestMapping("/intoLogin")
    public String intoLogin() {
        return "login";
    }

    //注册接口
    @RequestMapping("/intoRegister")
    public String intoRegister() {
        return "register";
    }

    @GetMapping("/intoCode")
    public String intoCode() {
        return "code";
    }

    /**
     * 接收前端代码
     *
     * @param codeJava
     * @return
     */
    @PostMapping("/compile")
    @ResponseBody
    public Result compile(@RequestBody JSONObject codeJava) {

        String code = (String) codeJava.get("code");


        CustomStringJavaCompiler compiler = new CustomStringJavaCompiler(code);
        boolean res = compiler.compiler();
        if (res) {
            System.out.println("编译成功");
            System.out.println("compilerTakeTime：" + compiler.getCompilerTakeTime());
            try {
                compiler.runMainMethod();
                System.out.println("runTakeTime：" + compiler.getRunTakeTime());
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(compiler.getRunResult());

            System.out.println("诊断信息：" + compiler.getCompilerMessage());
//            String data = JSON.toJSONString(Result.success(compiler.getRunResult()));

            return Result.success(compiler.getRunResult(), compiler.getRunTakeTime(), compiler.getCompilerTakeTime());
        } else {
            System.out.println("编译失败");
            System.out.println(compiler.getCompilerMessage());
//            String data2 = JSON.toJSONString(Result.error("500", "编译失败"));
            return Result.error("500", "编译失败");
        }


    }

    /**
     * 用户注册 管理
     * 管理员1 特定邮箱和账号
     * 用户2
     * <p>
     * 问题：为什么不需要将user添加到ioc 容器中
     * <p>
     * 功能：一、注册页面
     * 1.注册
     * 2. 保证无重复用户
     */

    @RequestMapping(value = "/register")
    @ResponseBody
    public Map<String, Object> register(@RequestBody User user) throws IOException {
        log.info("[controller]:用户开始注册操作");
        Map<String, Object> response = new HashMap<>();
        if (userService.testPhone(user.getPhone())) {
            log.warn("[controller]:注册失败，该号码已被注册");
            response.put("status", 0);
            response.put("message", "抱歉，该号码已被注册");
            return response;
        } else if (userService.testEmail(user.getEmail())) {
            log.warn("[controller]:注册失败，该邮箱已被注册");
            response.put("status", 0);
            response.put("message", "抱歉，该邮箱已被注册");
            return response;
        } else {
            String encodePassword = passwordEncoder.encode(user.getUserpassword());
            user.setUserpassword(encodePassword);//重新设置新密码
            //注册 否
            userService.toRegister(user);
            log.info("[controller]:用户-{}-注册成功", user.getUsername());
            //创建一个默认的收藏夹
            String result_add_category = categoryService.addDefaultCategory(user);
            response.put("status", 1);
            response.put("message", "注册成功！快去登陆吧");
            return response;
//            return "login";
        }
    }

    /**
     * 用户登录管理
     * 问题：传参的类的属性不全是否可以  逐个传参是否参数对其  不加@RequestBody是否可以
     * 怎样接收参数
     * <p>
     * 1.携带数据返回 没有@ResponseBody 就要使用session或者其他 携带数据
     * 返回的是json数据 怎样在前端页面接收
     * 2.是否有页面跳转 不能使用@ResponseBody
     */


    @RequestMapping(value = "/Login", produces = "application/json")
    public Object Login(@RequestParam("phone") String phone,
                        @RequestParam("userpassword") String userpassword,
                        HttpServletRequest request,
                        HttpSession session, Model model, HttpServletResponse httpServletResponse) {

        log.info("[controller]:用户开始登录操作,启用SpringSecurity");


        try {
            UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(phone, userpassword);//用户输入的token
            Authentication authentication = authenticationManager.authenticate(authToken);
            System.out.println("认证消息为：" + authentication);
            //手动存储到httpsession中
            // 将认证信息存储到 SecurityContext
            SecurityContext context = SecurityContextHolder.createEmptyContext();
            context.setAuthentication(authentication);
            SecurityContextHolder.setContext(context);

            // 手动存储到 HttpSession
            HttpSessionSecurityContextRepository repository = new HttpSessionSecurityContextRepository();
            repository.saveContext(context, request, httpServletResponse);


            CustomUserDetails user = (CustomUserDetails) SecurityContextHolder
                    .getContext()
                    .getAuthentication()
                    .getPrincipal();

            user.setId(categoryService.selectUserId(user));
            //查询 数据库中用户的个签
            String motto = userService.toSelectMotto(user.getPhone());
            session.setAttribute("motto", motto);

            //从数据库中得到pic
            String path = userService.toSelectPic(user.getPhone());
            System.out.println("pic" + path);
            session.setAttribute("pic", path);

            //登录成功 则可以使用 修改文章等功能，相应 内容需要改变
            //session传参 可以直接用表达式
            //通过session 后续可以判断到底登录没有 用户名也可以绑定文章
//                    session.setAttribute("user", user);//传参
            session.setAttribute("phone", user.getPhone());
            session.setAttribute("username", user.getUsername());
            session.setAttribute("pwd", user.getUserpassword());
            session.setAttribute("user", user);

            if (session.getAttribute("login_error_message") != null) {
                session.removeAttribute("login_error_message");
            }
            log.info("[controller]:用户-{}-登录成功", authentication.getName());
            return "main";
        } catch (Exception e) {
            if (e instanceof UsernameNotFoundException) {
                session.setAttribute("login_error_message", "登录失败,请检查电话号码和密码");
                model.addAttribute("login_error_message", "登录失败,请检查电话号码和密码");
                log.warn("[controller]:用户登录失败,未查询到该用户！");
                return "login";
            } else {
                session.setAttribute("login_error_message", e.getMessage());
                model.addAttribute("login_error_message", e.getMessage());
                log.error("[controller]:出错了，出错信息->{}", e.getMessage());
                return "login";
            }
        }
    }

    //退出账号
    @RequestMapping("loginOut")
    public String loginOut(HttpServletRequest request, Authentication authentication) {
        System.out.println("登出系统");
        if (authentication != null) {

            SecurityContextHolder.clearContext();
            log.info("用户-{}-执行登出操作，退出系统", request.getSession().getAttribute("username"));
            //去除session的user phone pic motto username
            request.getSession().removeAttribute("username");
            request.getSession().removeAttribute("phone");
            request.getSession().removeAttribute("pic");
            request.getSession().removeAttribute("motto");
            request.getSession().removeAttribute("user");
            request.getSession().setAttribute("hidden", "no");
            log.info("用户退出系统成功");
            return "forward:/blog/intoMain";
        }
        return "404";
    }


    //上传头像
    @RequestMapping("/updatePic")
    public String doUpload(MultipartFile photo, HttpSession session) throws IOException {
        if (!photo.isEmpty()) {
            String path = session.getServletContext().getRealPath("\\images");
//            String path = session.getServletContext().getRealPath("/") + "images/";
//            String path = "/images/";
            String filename = photo.getOriginalFilename();
            if (filename.endsWith(".jpg") || filename.endsWith(".png")) {
                File file = new File(path, filename);
                if (!file.exists()) {
                    file.mkdirs();
                }
                //上传
                //从session得到登录后存入session的phone
                String phone = (String) session.getAttribute("phone");
                System.out.println("存入 ？");
                //将路径存入数据库
                userService.toUpdatePic(phone, filename);
                photo.transferTo(file);
                //可以在session中取得数据 文件的名字加后缀
                //在他登录期间就可以显示自己的头像
                session.setAttribute("pic", filename);
            }
        }
//        返回到个人页面并刷新
        return "home";
    }

    //form标签绑定
//1.显示原来的数据
    @RequestMapping("/showInf")
    public ModelAndView changeInf(HttpSession session) {
        //根据session得到phone
        String phone = (String) session.getAttribute("phone");
        System.out.println(phone);
        //从数据库中得到 该用户的信息
        ModelAndView mv = new ModelAndView();
        CUser cUser = userService.showInf(phone);
        System.out.println("Cuser:" + cUser);
        //mv返回相关 CUser;
        mv.addObject("cUser", cUser);
        mv.setViewName("changeInf");
        return mv;
    }

    @RequestMapping("/changeInfo")
    public ModelAndView changeInfo(@ModelAttribute CUser cUser, HttpSession session) {
        System.out.println("cUser = " + cUser);

        log.info("[controller]:用户{}开始修改信息", session.getAttribute("username"));

        ModelAndView mv = new ModelAndView();

        String password = null;

        //修改用户名和密码 个签
        //更新到数据库
        String phone1 = (String) session.getAttribute("phone");

        if ((userService.toUpdateUser(cUser.getUsername(), passwordEncoder.encode(cUser.getUserpassword()), phone1))
                && (userService.toUpdateMotto(cUser.getContent(), phone1))) {
            session.setAttribute("username", cUser.getUsername());
            User user = (User) session.getAttribute("user");
            user.setUsername(cUser.getUsername());
            user.setUsername(cUser.getUsername());
            user.setUserpassword(passwordEncoder.encode(cUser.getUserpassword()));//对密码进行加密
            session.setAttribute("user", user);
            session.setAttribute("motto", cUser.getContent());
            mv.setViewName("home");
            //返回到个人页面 并刷新
            return mv;

        }
//        }
        mv.addObject("infoErro", "修改失败");
        mv.setViewName("home");
        //返回到个人页面 并刷新
        return mv;
        //再次修改session中的值
    }

    /**
     * 2. 发布博客 markdown
     * 测试：1.编写文章
     * 2.回显文章
     * 3.下载文章
     *
     * @return
     */
    @RequestMapping("/editor1")
    public String toTest() {
        return "editor";
    }

    /**
     * 发布博客 动态
     * 1.通过session存入作者信息以及时间方便后续读取
     * 2.存入数据库
     * 3.跳转页面到展示
     * <p>
     * 问题：session的存在时间
     * 退出时 将session中的值清空
     *
     * @param
     * @param
     * @return
     */

    @Value("${file.upload.path}")
    private String path;

    //上传图片
    @PostMapping("/uploadPic")
    public ResponseEntity<?> handleFileUpload(@RequestParam("editormd-image-file") MultipartFile file
            , HttpSession session) {
        System.out.println("进入上传");
        if (file.isEmpty()) {
            // 处理空文件的情况
            return new ResponseEntity<>("请选择一个文件上传", HttpStatus.BAD_REQUEST);
        }

        // 指定保存目录
//        String path = session.getServletContext().getRealPath("/images/pic");

        String originalFilename = file.getOriginalFilename();
        File dest = new File(path, originalFilename);

        try {
            file.transferTo(dest);
            // 返回图片的路径或URL（根据你的需求）
            String imageUrl = "{\"success\":1," +
                    "\"message\":\"上传成功\"," +
                    "\"url\":\"/images/pic/" + originalFilename + "\"}";
            return new ResponseEntity<>(imageUrl, HttpStatus.OK);
        } catch (IOException e) {
            e.printStackTrace();
            return new ResponseEntity<>("{\"success\":0}", HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    @RequestMapping(value = "/submitMarkdown", produces = "text/html;charset=utf-8")
    @ResponseBody
    public String markdown(@RequestBody Text text, HttpSession session) {
        log.info("上传文章 进行审核");
        text.setPhone((String) session.getAttribute("phone"));
        //2.通过session
//        session.setAttribute("text",text);
        //3.DTO
        TextWhole textWhole = new TextWhole();
        textWhole.setPhone(text.getPhone());
        textWhole.setTitle(text.getTitle());
        textWhole.setSubtitle(text.getSubtitle());
        textWhole.setContent(text.getContent());

        Date currentDateTime = new Date();
        textWhole.setCreatedata(currentDateTime);
        textWhole.setCid(text.getCid());
        textWhole.setMid(text.getMid());

        userService.toSave(textWhole);
        return "成功";
    }

    //1.0 测试编辑器
    @RequestMapping("/editor2")
    public String toTest1(String title, HttpSession session, Model model) {
        log.info("加载文章框架信息");

        TextWhole textWhole = userService.toSee(title);
        InfoText inf = userService.selectInf(title);
//        ModelAndView mv = new ModelAndView();
        model.addAttribute("status", textWhole.getStatus());
        model.addAttribute("title", title);
        model.addAttribute("subtitle", textWhole.getSubtitle());
        model.addAttribute("cid", inf.getCid());
        model.addAttribute("mid", inf.getMid());
        model.addAttribute("time", textWhole.getCreatedate());
        return "showmark";
    }


    //1.异步回显数据
    @RequestMapping(value = "/showMarkdown", produces = "text/html;charset=utf-8")
    @ResponseBody
    public String showmarkdown(String title, HttpServletRequest request) {
        log.info("回显文章{}", title);
//        System.out.println("进入回显");
//        System.out.println(title);
//从数据库中查询 ？ 内容 怎样从数据库里面查 通过时间和姓名
        //2.通过参数带回 可以用到全局
//        System.out.println(ctime);
        TextWhole textWhole = userService.toSee(title);
        //移除在session中的文章信息
        return textWhole.getContent();

    }

    //下载文章
//无法从前端得到文章mark
    @RequestMapping("download")
    public ResponseEntity<byte[]> downloadArticle() {
        //3.
        // 将文本内容转换为字节数组

        String con = "测试下载";
        byte[] content = con.getBytes(StandardCharsets.UTF_8);

        // 设置HTTP响应头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentDispositionFormData("attachment", "downloaded-text.txt"); // 设置为附件，并指定文件名
        headers.setContentType(MediaType.TEXT_PLAIN); // 文本文件类型

        // 创建响应实体
        return ResponseEntity.ok()
                .headers(headers)
                .body(content);
    }


    /**
     * 1.我的博客
     * 1.显示用户编写的博客
     * 2.通过博客title的超链接跳转到博客详情（实现为模板）
     */
    @RequestMapping("showHome")
    public String showHome() {
        return "home";
    }

    /**
     * 进入主页
     *
     * @param model
     * @param request
     * @param httpSession
     * @return
     */
    @RequestMapping("/showText")
    public ModelAndView showBolg(Model model, HttpServletRequest request, HttpSession httpSession) {
        log.info("[controller]:进入用户页面，需要查询文章和收藏夹");

//        查询用户的文章
        List<TextWhole> textWholeList = userService.toSeeAll((String) request.getSession().getAttribute("phone"));
        ModelAndView mv = new ModelAndView();
        List<TextWhole> texts = new ArrayList<>();
        List<TextWhole> drafts = new ArrayList<>();
        Random random = new Random();
        if (!textWholeList.isEmpty()) {
            //设置图片 查询文章分类
            for (int i = 0; i < textWholeList.size(); i++) {
                if (textWholeList.get(i).getStatus() == 0) {
                    int randomNumber = random.nextInt(1, 12);
                    textWholeList.get(i).setNumber(randomNumber);
                    InfoText info = userService.selectInf(textWholeList.get(i).getTitle());
                    textWholeList.get(i).setMid(info.getMid());
                    textWholeList.get(i).setCid(info.getCid());
                    // 添加到草稿
                    drafts.add(textWholeList.get(i));
                } else {
                    InfoText info = userService.selectInf(textWholeList.get(i).getTitle());
                    int randomNumber = random.nextInt(1, 12);
                    textWholeList.get(i).setNumber(randomNumber);
                    textWholeList.get(i).setMid(info.getMid());
                    textWholeList.get(i).setCid(info.getCid());
                    texts.add(textWholeList.get(i));
                }
            }
            if (!texts.isEmpty()) {
                mv.addObject("tHid", "d-none");
            }
            if (!drafts.isEmpty()) {
                mv.addObject("dHid", "d-none");
            }
            mv.addObject("texts", texts);
            mv.addObject("drafts", drafts);
        }
        //查询用户的收藏夹
        User user = (User) httpSession.getAttribute("user");
        categoryService.showCategory(user, httpSession);
        mv.setViewName("home");
        return mv;
    }

//    1.2显示其他博主的博客

    @RequestMapping("/showOther/{othername}")
    public String showOther(@PathVariable String othername, Model model, HttpServletRequest request) {
        //数据库查询
        String phone = userService.selectPhone(othername);
        //查询数据中用户的文章 根据电话
        List<TextWhole> textWholeList = userService.toSeeAll(phone);
//        将文章列表添加到Model中，以便在视图中访问
        model.addAttribute("texts", textWholeList);
        //session设置 集合 title 时间
        //返回
        return "author_home";
    }

    //2.查看自己的博客
    @RequestMapping("/showContent")
    public ModelAndView showContent(String title, HttpSession session) {
        TextWhole textWhole = userService.toSee(title);
        InfoText inf = userService.selectInf(title);
        ModelAndView mv = new ModelAndView();

        mv.addObject("status", textWhole.getStatus());
        mv.addObject("subtitle", textWhole.getSubtitle());
        mv.addObject("cid", inf.getCid());
        mv.addObject("mid", inf.getMid());
        mv.addObject("title", title);

        Text text = new Text();
        text.setTitle(title);
        text.setId(commitService.selectTextIdByTitle(text));
        mv.addObject("article_id", text.getId());
        mv.addObject("commit_recover_map", commitService.selectCommitAndRecover(text));
        mv.setViewName("showmark");
        return mv;
    }


    //2.通过链接跳转到博客内容
    @RequestMapping("/showContent2")
    public ModelAndView showContent(String title, String phone, String sub, String time, HttpSession session) {
        log.info("进入博主{}的博客--{}", phone, title);
        User user = userMapper.selectUser(phone);
        ModelAndView mv = new ModelAndView();
        if (user.getMotto() == null) {
            mv.addObject("motto", "此人无个签");
        } else {
            mv.addObject("motto", user.getMotto());
        }
//        通过 tile mid cid
        InfoText inf = userService.selectInf(title);
        mv.addObject("otherPath", user.getPic());
        mv.addObject("title", title);
        mv.addObject("author", user.getUsername());
        mv.addObject("time", time);
        mv.addObject("subtitle", sub);
        mv.addObject("cid", inf.getCid());
        mv.addObject("mid", inf.getMid());
        mv.addObject("hisPhone", phone);

        Text text = new Text();
        text.setTitle(title);
        text.setId(commitService.selectTextIdByTitle(text));
        session.setAttribute("article_id", text.getId());
        Map<Commit, List<Recover>> map = commitService.selectCommitAndRecover(text);
        mv.addObject("commit_recover_map", map);
        mv.setViewName("showblog");
        return mv;
    }


    /**
     * 3.删除 修改博客
     * 1.通过a标签直接跳转携带标题
     */
    @RequestMapping("/deleteText")
//    @ResponseBody
    public ResponseEntity<?> deleteText(String title) {
        System.out.println(title);

        //删回复 评论
        Text text = new Text();
        text.setTitle(title);
        text.setId(commitService.selectTextIdByTitle(text));
        Map<Commit, List<Recover>> map;
        map = commitService.selectCommitAndRecover(text);

        for (Map.Entry entry : map.entrySet()) {
            //分别得到键值
            List<Recover> recovers = (List<Recover>) entry.getValue();
            Commit commit = (Commit) entry.getKey();
            //删回复
            for (Recover recover : recovers) {
                commitService.DeleteRecover(recover, commit);
            }
            System.out.println("回复已删除");
            //删评论
            commitService.DeleteCommit(commit);
            System.out.println("评论已删除");
        }

        //删除从数据库中
        //todo
        boolean success = userService.toDelete(title);

        // 创建一个响应对象
        Map<String, Object> response = new HashMap<>();
        response.put("success", success);
        if (!success) {
            // 可以添加额外的错误详情或消息
            response.put("message", "删除博客失败");
        }

        // 返回响应实体，其中包含了状态码和响应体
        return new ResponseEntity<>(response, HttpStatus.OK);
    }

    /**
     * 修改 博客
     *
     * @param title
     * @param session
     * @return
     */
//跳转到编辑页面
    @RequestMapping("/changeText")
    public String changeText(String title, HttpSession session) {
        //拿出数据显示到修改页面

        System.out.println(title);
        //修改从数据库中
        //通过title拿出  所有TextWhole，显示
        //todo
        TextWhole textWhole = userService.toSee(title);

        session.setAttribute("title", textWhole.getTitle());
        session.setAttribute("phone", textWhole.getPhone());
        session.setAttribute("subtitle", textWhole.getSubtitle());
        //将时间存入session 和用户名一起 提取文章 显示
        session.setAttribute("time", textWhole.getCreatedate());
        session.setAttribute("content", textWhole.getContent());
        session.setAttribute("cid", textWhole.getCid());
        session.setAttribute("mid", textWhole.getMid());
        return "content";
    }

    //更新博客
    @RequestMapping("uploadText")
    public ResponseEntity<?> uploadText(@RequestBody Text text, HttpSession session) {
//        System.out.println("修改博客 controller");
        log.info("修改博客");
        //到数据库更新
//        System.out.println(text);
        String phone = (String) session.getAttribute("phone");
//        System.out.println(phone);
        //优化：也可以改题目 办法 在这一层想办法通过什么查询到修改了文章的题目的id

        //2.通过session
        //3.DTO
        TextWhole textWhole = new TextWhole();
        textWhole.setPhone(phone);
        textWhole.setTitle(text.getTitle());
        textWhole.setSubtitle(text.getSubtitle());
        textWhole.setContent(text.getContent());

        Date currentDateTime = new Date();
        textWhole.setCreatedata(currentDateTime);
        textWhole.setCid(text.getCid());
        textWhole.setMid(text.getMid());

        //更新数据库
        //todo text home_page
        boolean result = userService.toUpDate(textWhole, phone);

        // 创建一个响应对象
        Map<String, Object> response = new HashMap<>();
        response.put("result", result);
        response.put("url", "http://localhost:8080/blog/intoMain");
        if (!result) {
            // 可以添加额外的错误详情或消息
            response.put("info", "修改失败");

        }
        // 返回响应实体，其中包含了状态码和响应体
        return new ResponseEntity<>(response, HttpStatus.OK);
    }

    //返回个人页面
    @RequestMapping("returnP")
    public String returnPeople() {
        return "home";
    }
}
